// axios配置  可自行根据项目进行更改，只需更改该文件即可，其他文件可以不动
// The axios configuration can be changed according to the project, just change the file, other files can be left unchanged

import type { AxiosInstance, AxiosResponse } from "axios";
import { clone } from "lodash-es";
import axios from "axios";
import type { AxiosTransform, CreateAxiosOptions } from "./axiosTransform";
import { VAxios } from "./Axios";
import { checkStatus } from "./checkStatus";
import { formatRequestDate, joinTimestamp } from "./helper";
import type { RequestOptions, Result } from "@/types/axios";
import { ContentTypeEnum, RequestEnum, ResultEnum } from "@/enums/httpEnum";
import { deepMerge, setObjToUrlParams } from "@/utils";
import { store } from "@/redux";
import { useMessage } from "@/hooks/web/useMessage";
import { isEmpty, isNull, isString, isUndefined } from "@/utils/is";

const tenantEnable = import.meta.env.VITE_GLOB_APP_TENANT_ENABLE as string;
const { createMessage, createErrorModal, createSuccessModal } = useMessage();

// 请求白名单，无须token的接口
const whiteList: string[] = ["/login", "/refresh-token"];
// 租户Id
const tenantId = 1;

/**
 * @description: 数据处理，方便区分多种处理方式
 */
const transform: AxiosTransform = {
	/**
	 * @description: 处理响应数据。如果数据不是预期格式，可直接抛出错误
	 */
	transformResponseHook: (res: AxiosResponse<Result>, options: RequestOptions) => {
		const { isTransformResponse, isReturnNativeResponse } = options;
		// 二进制数据则直接返回
		if ((res.request.responseType === "blob" || res.request.responseType === "arraybuffer") && res.data?.code === undefined)
			// 这个判断的目的是：excel 导出等情况下，系统执行异常，此时返回的是 json，而不是二进制数据
			return res.data;

		// 是否返回原生响应头 比如：需要获取响应头时使用该属性
		if (isReturnNativeResponse) return res;

		// 不进行任何处理，直接返回
		// 用于页面代码可能需要直接获取code，data，message这些信息时开启
		if (!isTransformResponse) return res.data;

		const { data } = res;
		//  这里 code，result，message为 后台统一的字段，需要在 types.ts内修改为项目自己的接口返回格式
		const { code, data: result, msg } = data;
		// 这里逻辑可以根据项目进行修改
		const hasSuccess = data && Reflect.has(data, "code") && code === ResultEnum.SUCCESS;
		if (hasSuccess) {
			let successMsg = msg;

			if (isNull(successMsg) || isUndefined(successMsg) || isEmpty(successMsg)) successMsg = "Operation success";

			if (options.successMessageMode === "modal") createSuccessModal({ title: "Success Tip", content: successMsg });
			else if (options.successMessageMode === "message") createMessage.success(successMsg);

			return result;
		}

		// errorMessageMode='modal' 的时候会显示modal错误弹窗，而不是消息提示，用于一些比较重要的错误
		// errorMessageMode='none' 一般是调用时明确表示不希望自动弹出错误提示
		if (options.errorMessageMode === "modal") createErrorModal({ title: "Error Tip", content: msg });
		else if (options.errorMessageMode === "message") createMessage.error(msg);

		throw new Error(data.msg);
	},

	// 请求之前处理config
	beforeRequestHook: (config, options) => {
		const { joinParamsToUrl, formatDate, joinTime = true } = options;
		const params = config.params || {};
		const data = config.data || false;
		formatDate && data && !isString(data) && formatRequestDate(data);
		if (config.method?.toUpperCase() === RequestEnum.GET) {
			if (!isString(params)) {
				// 给 get 请求加上时间戳参数，避免从缓存中拿数据。
				let url = `${config.url}?`;
				for (const propName of Object.keys(params)) {
					const value = params[propName];

					if (value !== void 0 && value !== null && typeof value !== "undefined") {
						if (typeof value === "object") {
							for (const val of Object.keys(value)) {
								const paramss = `${propName}[${val}]`;
								const subPart = `${encodeURIComponent(paramss)}=`;
								url += `${subPart + encodeURIComponent(value[val])}&`;
							}
						} else {
							url += `${propName}=${encodeURIComponent(value)}&`;
						}
					}
				}
				url = url.slice(0, -1);
				config.params = {};
				config.url = url;
			} else {
				// 兼容restful风格
				config.url = `${config.url + params}${joinTimestamp(joinTime, true)}`;
				config.params = undefined;
			}
		} else {
			if (!isString(params)) {
				formatDate && formatRequestDate(params);
				if (
					Reflect.has(config, "data") &&
					config.data &&
					(Object.keys(config.data).length > 0 || config.data instanceof FormData)
				) {
					config.data = data;
					config.params = params;
				} else {
					// 非GET请求如果没有提供data，则将params视为data
					config.data = params;
					config.params = undefined;
				}
				if (joinParamsToUrl) {
					config.url = setObjToUrlParams(config.url as string, Object.assign({}, config.params, config.data));
				}
			} else {
				// 兼容restful风格
				config.url = config.url + params;
				config.params = undefined;
			}
		}
		return config;
	},

	/**
	 * @description: 请求拦截器处理
	 */
	requestInterceptors: (config, options) => {
		// 是否需要设置 token
		let isToken = (config as Recordable)?.requestOptions?.withToken === false;
		isToken = whiteList.some(v => {
			if (config.url) {
				config.url.includes(v);
				return false;
			}
			return true;
		});
		// 请求之前处理config
		const {
			user: { accessToken: token },
			global: { language }
		} = store.getState();
		if (token && !isToken) {
			// jwt token
			(config as Recordable).headers.Authorization = options.authenticationScheme
				? `${options.authenticationScheme} ${token}`
				: token;
		}
		// 设置租户
		if (tenantEnable && tenantEnable === "true") {
			(config as Recordable).headers["tenant-id"] = tenantId || "1";
		}
		// 设置语言
		(config as Recordable).headers["Accept-Language"] = language || "en";

		return config;
	},

	/**
	 * @description: 响应拦截器处理
	 */
	responseInterceptors: (res: AxiosResponse<any>) => {
		return res;
	},

	/**
	 * @description: 响应错误处理
	 */
	responseInterceptorsCatch: (axiosInstance: AxiosInstance, error: any) => {
		const { code, message, config } = error || {};
		const errorMessageMode = config?.requestOptions?.errorMessageMode || "none";
		const err: string = error?.toString?.() ?? "";
		let errMessage = "";

		if (axios.isCancel(error)) return Promise.reject(error);

		try {
			if (code === "ECONNABORTED" && message.includes("timeout"))
				errMessage = "The interface request timed out, please refresh the page and try again!";

			if (err?.includes("Network Error"))
				errMessage = "Please check if your network connection is normal! The network is abnormal";

			if (errMessage) {
				if (errorMessageMode === "modal") createErrorModal({ title: "Error Tip", content: errMessage });
				else if (errorMessageMode === "message") createMessage.error(errMessage);

				return Promise.reject(error);
			}
		} catch (error) {
			throw new Error(error as unknown as string);
		}

		checkStatus(error?.response?.status);

		return Promise.reject(error);
	}
};

function createAxios(opt?: Partial<CreateAxiosOptions>) {
	return new VAxios(
		// 深度合并
		deepMerge(
			{
				// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
				// authentication schemes，e.g: Bearer
				authenticationScheme: "Bearer",
				timeout: 10 * 1000,
				// 基础接口地址
				baseURL: import.meta.env.VITE_API_URL as string,
				headers: { "Content-Type": ContentTypeEnum.JSON },
				// 数据处理方式
				transform: clone(transform),
				// 配置项，下面的选项都可以在独立的接口请求中覆盖
				requestOptions: {
					// 默认将prefix 添加到url
					joinPrefix: true,
					// 是否返回原生响应头 比如：需要获取响应头时使用该属性
					isReturnNativeResponse: false,
					// 需要对返回数据进行处理
					isTransformResponse: true,
					// post请求的时候添加参数到url
					joinParamsToUrl: false,
					// 格式化提交参数时间
					formatDate: true,
					// 消息提示类型
					errorMessageMode: "message",
					//  是否加入时间戳
					joinTime: true,
					// 忽略重复请求
					ignoreCancelToken: true,
					// 是否携带token
					withToken: true,
					retryRequest: {
						isOpenRetry: true,
						count: 5,
						waitTime: 100
					}
				}
			},
			opt || {}
		)
	);
}
export const defHttp = createAxios();
